package craft;

import java.io.CharArrayReader;
import java.util.ArrayList;
import java.util.List;

public class SimpleLexer {
    // 在解析过程中(initToken和tokenize)用到的临时变量
    private StringBuffer tokenText = null;          // 临时保存token的文本
    private List<Token> tokens = null;              // 保存解析出来的token
    private SimpleToken token = null;               // 当前正在解析的token


    public static void main(String[] args) {
        SimpleLexer lexer = new SimpleLexer();

        String script = "int age = 45;";
        System.out.println("parse: " + script);
        SimpleTokenReader tokenReader = lexer.tokenize(script);
        dump(tokenReader);

        //测试inta的解析
        script = "inta age = 45;";
        System.out.println("\nparse: " + script);
        tokenReader = lexer.tokenize(script);
        dump(tokenReader);

        //测试in的解析
        script = "in age = 45;";
        System.out.println("\nparse: " + script);
        tokenReader = lexer.tokenize(script);
        dump(tokenReader);

        //测试>=的解析
        script = "age >= 45;";
        System.out.println("\nparse: " + script);
        tokenReader = lexer.tokenize(script);
        dump(tokenReader);

        //测试>的解析
        script = "age > 45;";
        System.out.println("\nparse: " + script);
        tokenReader = lexer.tokenize(script);
        dump(tokenReader);
    }


    /**
     * 判断当前字符是否为字母
     * @param ch
     * @return
     */
    public boolean isAlpha(int ch) {
        return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z');
    }


    /**
     * 判断当前字符是否为数字
     * @param ch
     * @return
     */
    public boolean isDigit(int ch) {
        return ch >= '0' && ch <= '9';
    }

    /**
     * 判断当前字符是否为空白字符
     * @param ch
     * @return
     */
    public boolean isBlank(int ch) {
        return ch == ' ' || ch == '\r' || ch == '\n';
    }


    /**
     * 有限状态机进入初始状态
     * 在初始状态，并不做停留，马上进入其他状态
     * 开始解析的时候，进入初始状态；某个token解析完，也进入初始状态，在这里把token记下来，然后建立一个新的token
     * @param ch
     * @return
     */
    public DfaState initToken(char ch) {
        if (tokenText.length() > 0) {
            // 保存刚解析好的一个token
            token.text = tokenText.toString();
            tokens.add(token);

            // 再创建新的临时token对象
            token = new SimpleToken();
            tokenText = new StringBuffer();
        }

        DfaState newState = DfaState.Initial;

        if (isAlpha(ch)) {
            if (ch == 'i') {
                newState = DfaState.Id_int1;        // 用三个状态判断是否是关键字 int
            } else {
                newState = DfaState.Id;
            }
            token.type = TokenType.Identifier;
            tokenText.append(ch);
        } else if (isDigit(ch)) {
            newState = DfaState.IntLiteral;
            token.type = TokenType.IntLiteral;
            tokenText.append(ch);
        } else if (ch == '>') {
            newState = DfaState.GT;
            token.type = TokenType.GT;
            tokenText.append(ch);
        } else if (ch == '+') {
            newState = DfaState.Plus;
            token.type = TokenType.Plus;
            tokenText.append(ch);
        } else if (ch == '-') {
            newState = DfaState.Minus;
            token.type = TokenType.Minus;
            tokenText.append(ch);
        } else if (ch == '*') {
            newState = DfaState.Star;
            token.type = TokenType.Star;
            tokenText.append(ch);
        } else if (ch == '/') {
            newState = DfaState.Slash;
            token.type = TokenType.Slash;
            tokenText.append(ch);
        } else if (ch == ';') {
            newState = DfaState.SemiColon;
            token.type = TokenType.SemiColon;
            tokenText.append(ch);
        } else if (ch == '(') {
            newState = DfaState.LeftParen;
            token.type = TokenType.LeftParen;
            tokenText.append(ch);
        } else if (ch == ')') {
            newState = DfaState.RightParen;
            token.type = TokenType.RightParen;
            tokenText.append(ch);
        } else if (ch == '=') {
            newState = DfaState.Assignment;
            token.type = TokenType.Assignment;
            tokenText.append(ch);
        } else {
            // 跳过所有的未知模式
           newState = DfaState.Initial;
        }
        return newState;
    }

    /**
     * 解析字符串，形成Token
     * 原理：有限状态自动机，在不同的状态中迁移
     * 每调用一次initToken，都会把刚解析好的Token保存下来，并从初始状态再开始解析
     * @param code
     * @return
     */
    public SimpleTokenReader tokenize(String code) {
        tokens = new ArrayList<Token>();
        CharArrayReader reader = new CharArrayReader(code.toCharArray());
        tokenText = new StringBuffer();
        token = new SimpleToken();
        int ich = 0;
        char ch = 0;

        DfaState state = DfaState.Initial;

        try {
           while ((ich = reader.read()) != -1) {
               ch = (char) ich;
               switch (state) {
                   case Initial:
                       state = initToken(ch);
                       break;
                   case Id:
                       // 若状态为Id，后面还是数字或字母，保持Id状态不变
                       if (isAlpha(ch) || isDigit(ch)) {
                           tokenText.append(ch);
                       } else {
                           state = initToken(ch);
                       }
                       break;
                   case GT:
                       if (ch == '=') {
                           token.type = TokenType.GE;
                           state = DfaState.GE;
                           tokenText.append(ch);
                       } else {
                           state = initToken(ch);
                       }
                       break;
                   case GE:
                   case Assignment:
                   case Plus:
                   case Minus:
                   case Star:
                   case Slash:
                   case SemiColon:
                   case LeftParen:
                   case RightParen:
                       state = initToken(ch);
                       break;
                   case IntLiteral:
                       if (isDigit(ch)) {
                           // 继续保持在数值状态
                           tokenText.append(ch);
                       } else {
                           state = initToken(ch);
                       }
                       break;
                   case Id_int1:
                       if (ch == 'n') {
                           state = DfaState.Id_int2;
                           tokenText.append(ch);
                       } else if (isAlpha(ch) || isDigit(ch)) {
                            state = DfaState.Id;
                            tokenText.append(ch);
                       } else {
                           state = initToken(ch);
                       }
                       break;
                   case Id_int2:
                       if (ch == 't') {
                           state = DfaState.Id_int3;
                           tokenText.append(ch);
                       } else if (isAlpha(ch) || isDigit(ch)) {
                           state = DfaState.Id;
                           tokenText.append(ch);
                       } else {
                           state = initToken(ch);
                       }
                       break;
                   case Id_int3:
                       if (isBlank(ch)) {
                           token.type = TokenType.Int;
                           state = initToken(ch);
                       } else {
                           // 到了INT状态，若后面又紧跟着字母或者数字，则跳到ID状态
                           state = DfaState.Id;
                           tokenText.append(ch);
                       }
                       break;
                   default:
               }
           }
           // 把最后一个token送进去
            if (tokenText.length() > 0) {
                initToken(ch);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return new SimpleTokenReader(tokens);
    }


    /**
     * 打印所有的Token
     * @param tokenReader
     */
    public static void dump(SimpleTokenReader tokenReader) {
        if (tokenReader != null) {
            System.out.println("text\ttype");
            Token token = tokenReader.read();
            while (token != null) {
                System.out.println(token.getText() + "\t\t" + token.getType());
                token = tokenReader.read();
            }
        }
    }


}
